package com.king.edu.filter;

import com.king.edu.domain.LimitedQueue;
import com.king.edu.domain.TPMain;
import org.apache.dubbo.rpc.*;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

/**
 * @ClassName: TPMonitorFilter
 * @Description: TODO
 * @Author: wangchangyong
 * @Date: 2020/6/8 21:44
 * @Version: 1.0.0
 **/
// @Activate(group = {CommonConstants.CONSUMER})
public class TPMonitorFilter implements Filter, Runnable {

    private static LimitedQueue<TPMain> methodARespTime = new LimitedQueue<>(2000);
    private static LimitedQueue<TPMain> methodBRespTime = new LimitedQueue<>(2000);
    private static LimitedQueue<TPMain> methodCRespTime = new LimitedQueue<>(2000);

    public TPMonitorFilter() {
        // 每隔5s打印最近1分钟内每个方法的TP90、TP99的耗时情况
        Executors.newSingleThreadScheduledExecutor()
                .scheduleWithFixedDelay(this, 5, 5, TimeUnit.SECONDS);
    }

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        String methodName = RpcContext.getContext().getAttachment("methodName");
        long startTime = System.currentTimeMillis();
        // 执行方法
        Result invoke = invoker.invoke(invocation);
        // System.out.println("invoke method " + methodName + "time = " + (System.currentTimeMillis() - startTime) + "毫秒");
        if (invoke != null) {
            // 同步返回记录请求耗时
            long time = System.currentTimeMillis() - startTime;
            switch (methodName) {
                case "methodA":
                    methodARespTime.add(new TPMain(System.currentTimeMillis(), time));
                    break;
                case "methodB":
                    methodBRespTime.add(new TPMain(System.currentTimeMillis(), time));
                    break;
                case "methodC":
                    methodCRespTime.add(new TPMain(System.currentTimeMillis(), time));
                    break;
                default:
                    break;
            }
        }
        return invoke;
    }

    @Override
    public void run() {
        LocalDateTime now = LocalDateTime.now();
        try {
            List<Long> methodATimes = methodTime_1min(methodARespTime);
            long methodATP90 = tp(methodATimes, 90);
            long methodATP99 = tp(methodATimes, 99);
            System.out.println(now + " invoke method = methodA, TP90 = " + methodATP90 + "毫秒");
            System.out.println(now + " invoke method = methodA, TP99 = " + methodATP99 + "毫秒");

            List<Long> methodBTimes = methodTime_1min(methodBRespTime);
            long methodBTP90 = tp(methodBTimes, 90);
            long methodBTP99 = tp(methodBTimes, 99);
            System.out.println(now + " invoke method = methodB, TP90 = " + methodBTP90 + "毫秒");
            System.out.println(now + " invoke method = methodB, TP99 = " + methodBTP99 + "毫秒");

            List<Long> methodCTimes = methodTime_1min(methodCRespTime);
            long methodCTP90 = tp(methodCTimes, 90);
            long methodCTP99 = tp(methodCTimes, 99);
            System.out.println(now + " invoke method = methodC, TP90 = " + methodCTP90 + "毫秒");
            System.out.println(now + " invoke method = methodC, TP99 = " + methodCTP99 + "毫秒");
            System.out.println("--------------------------------------------------------------");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取最近1分钟内每个方法请求耗时
     * @return
     */
    public static List<Long> methodTime_1min(LimitedQueue<TPMain> respTime) {
        List<Long> methodTime = new ArrayList<>();
        if (respTime == null || respTime.size() == 0) {
            return methodTime;
        }

        long now = System.currentTimeMillis();
        long beforeNow = now - 60000;

        for (int i = 0; i< respTime.size();i++) {
            TPMain tpMain = respTime.get(i);
            long currentTime = tpMain.getCurrentTime();
            long execTime = tpMain.getExecTime();
            if (currentTime >= beforeNow && currentTime <= now) {
                methodTime.add(execTime);
            } else if (currentTime < beforeNow) {
                continue;
            } else {
                break;
            }
        }
        return methodTime;
    }


    /**
     * 获取TP
     * @param times
     * @param percent
     * @return
     */
    private static long tp(List<Long> times, int percent) {
        if (times == null || times.size() == 0) {
            return 0L;
        }
        float percentF = (float) percent / 100;
        int index = (int) (percentF * times.size()) > 0 ? (int) (percentF * times.size()) - 1 : 0;
        Collections.sort(times);
        return times.get(index);
    }

}
